package support;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.mathworks.toolbox.javabuilder.MWException;
import com.mathworks.toolbox.javabuilder.MWNumericArray;

import util.Lang;
import util.MathUtil;
import net.sf.javaml.clustering.KMeans;
import net.sf.javaml.core.Dataset;
import net.sf.javaml.core.DefaultDataset;
import net.sf.javaml.core.Instance;
import net.sf.javaml.core.SparseInstance;

public class Aggregate {

	/**
	 * Cluster demands using demand sum sort
	 * **/
	public Map<Integer, List<Double[]>> sumSort(double[][] data, int groups) {
		Map<Integer, List<Double[]>> result = new HashMap<Integer, List<Double[]>>();
		List<Integer> indexSort = new ArrayList<>();
		List<Double> valueSort = new ArrayList<>();
		for (int i = 0; i < data.length; i++) {
			double sum = MathUtil.sum(data[i]);
			int index = 0;
			boolean inserted = false;
			for (index = 0; index < valueSort.size(); index++) {
				if (valueSort.get(index) > sum) {
					valueSort.add(index, sum);
					indexSort.add(index, i);
					inserted = true;
					break;
				}
			}
			if (!inserted) {
				indexSort.add(index);
				valueSort.add(sum);
			}
		}
		int maxCountInAGroup = (int) Math.ceil((double) data.length / groups);
		int index = 0;
		for (int g = 0; g < groups; g++) {
			List<Double[]> list = new ArrayList<>();
			int count = 0;
			for (; index < indexSort.size(); index++) {
				if (count >= maxCountInAGroup) {
					count = 0;
					break;
				}
				list.add(Lang.convertdouble2Double(data[indexSort.get(index)]));
				count++;
			}
			result.put(g, list);
		}

		return result;
	}

	/**
	 * Calling matlab k-means function to cluster
	 * 
	 * @throws MWException
	 * **/
	public Map<Integer, List<Double[]>> kmeans_matlab(double[][] data,
			int groups) {
		Map<Integer, List<Double[]>> result = new HashMap<Integer, List<Double[]>>();
		try {
			Matlab.KMeans mKmeans = new Matlab.KMeans();
			Object[] idx = mKmeans.KMEANS(1, data, groups);
			int[] idx_int = ((MWNumericArray) idx[0]).getIntData();
			for (int i = 0; i < data.length; i++) {
				int group = idx_int[i] - 1;
				if (!result.containsKey(group))
					result.put(group, new ArrayList<Double[]>());
				result.get(group).add(Lang.convertdouble2Double(data[i]));
			}
			return result;
		} catch (MWException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}

	}

	/**
	 * Use k-means algorithm to aggregate the data
	 * **/
	public Map<Integer, List<Double[]>> kmeans(double[][] data, int groups) {
		Map<Integer, List<Double[]>> result = new HashMap<Integer, List<Double[]>>();

		KMeans kmeans = new KMeans(groups);
		Dataset dataSet = new DefaultDataset();
		for (int i = 0; i < data.length; i++) {
			Instance ins = new SparseInstance(data[i]);
			dataSet.add(ins);
		}
		Dataset[] clusters = kmeans.cluster(dataSet);
		for (int i = 0; i < clusters.length; i++) {
			List<Double[]> list = new ArrayList<Double[]>();
			Dataset ds = clusters[i];
			for (int j = 0; j < ds.size(); j++) {
				Double[] values = ds.get(j).values()
						.toArray(new Double[ds.get(j).values().size()]);
				list.add(values);
			}
			result.put(i, list);
		}

		return result;
	}

	/**
	 * Use round-robin method to aggregate the data
	 * **/
	public Map<Integer, List<Double[]>> roundRobin(double[][] data, int groups) {
		Map<Integer, List<Double[]>> result = new HashMap<Integer, List<Double[]>>();

		for (int g = 0; g < groups; g++) {
			result.put(g, new ArrayList<Double[]>());
		}
		for (int i = 0; i < data.length; i++) {
			int gidx = i % groups;
			result.get(gidx).add(Lang.convertdouble2Double(data[i]));
		}

		return result;
	}
}
